﻿using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Spencen.Mobile.UI.Primitives;
using Spencen.Mobile.UI.Animations;
using Spencen.Mobile.UI.Easings;

namespace Spencen.Mobile.UI.Behaviors
{
    public class ScrollBehavior : Behavior
    {
        private DrawingElement _selectedElement;
        private bool _selectedElementOriginalIsRetained;
        private Point _dragOffset;
        private bool _isMouseDown;
        private bool _isDragging;

        public ScrollBehavior ()
	    {
            StartDragThreshold = 10;
	    }

        public int StartDragThreshold { get; set; }
        public ScrollBars DragAxis { get; set; }

        public override void OnAttached( DrawingElement target )
        {
            base.OnAttached( target );

            target.MouseDown += target_MouseDown;
            target.MouseUp += target_MouseUp;
            target.MouseMove += target_MouseMove;

            var drawingContainer = target as DrawingContainer;
            if ( drawingContainer != null )
                drawingContainer.HitTestOnContainer = true;
        }

        public override void OnDettached( DrawingElement target )
        {
            base.OnDettached( target );

            target.MouseDown -= target_MouseDown;
            target.MouseUp -= target_MouseUp;
            target.MouseMove -= target_MouseMove;
        }

        private void target_MouseDown( object sender, MouseEventArgs e )
        {
            if ( e.Button == MouseButtons.Left )
            {
                _selectedElement = sender as DrawingElement;
                if ( _selectedElement != null )
                    _dragOffset = new Point( e.X - _selectedElement.Position.X, e.Y - _selectedElement.Position.Y );

                _isMouseDown = true;
            }
        }

        private void target_MouseUp( object sender, MouseEventArgs e )
        {
            if ( _selectedElement != null )
            {
                var parent = _selectedElement.Parent;
                if ( parent != null )
                {
                    var constrainedPosition = _selectedElement.Position;
                    if ( DragAxis == ScrollBars.Vertical )
                    {
                        if ( _selectedElement.Position.Y > 0 )
                            constrainedPosition.Y = 0;
                        else if ( ( _selectedElement.Position.Y + _selectedElement.Size.Height ) < parent.Size.Height && _selectedElement.Size.Height < parent.Size.Height )
                            constrainedPosition.Y = parent.Size.Height - _selectedElement.Size.Height;
                    }

                    var moveAnimation = new PointAnimation( _selectedElement, "Position", new TimeSpan( 0, 0, 1 ) )
                    {
                        FinalValue = constrainedPosition,
                        EasingFunction = new ElasticEasing()
                    };
                    moveAnimation.Completed = new Action<Animation>( ScrollAnimationCompleted );
                    _selectedElement.Host.AnimationManager.AddAnimation( moveAnimation, true );
                }

                _selectedElement = null;
                _isMouseDown = false;
                _isDragging = false;
            }
        }

        private void ScrollAnimationCompleted( Animation moveAnimation )
        {
            var targetElement = (DrawingElement) moveAnimation.Target;
            targetElement.IsRetained = _selectedElementOriginalIsRetained;
            targetElement.Invalidate();
        }

        private void target_MouseMove( object sender, MouseEventArgs e )
        {
            if ( _selectedElement == sender && _isMouseDown )
            {
                var mouseOffset = new Point( e.X - _dragOffset.X - _selectedElement.Position.X, e.Y - _dragOffset.Y - _selectedElement.Position.Y );
                var newPosition = new Point(
                    _selectedElement.Position.X + ( ( DragAxis == ScrollBars.Both || DragAxis == ScrollBars.Horizontal ) ? mouseOffset.X : 0 ),
                    _selectedElement.Position.Y + ( ( DragAxis == ScrollBars.Both || DragAxis == ScrollBars.Vertical ) ? mouseOffset.Y : 0 ) );

                // Have we initiated a drag?
                if ( !_isDragging && DrawingSupport.DistanceBetweenPoints( newPosition, _selectedElement.Position ) > StartDragThreshold )
                {
                    _isDragging = true;
                    _selectedElementOriginalIsRetained = _selectedElement.IsRetained;
                    _selectedElement.IsRetained = true;
                }

                if ( _isDragging )
                {
                    _selectedElement.Invalidate(); // Invalidate the old position
                    _selectedElement.Position = newPosition;
                    _selectedElement.Invalidate(); // Invalidate the new position
                }
            }
        }
    }
}
